package viewer.renderer;

import java.io.*;
import java.util.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import com.sun.opengl.util.*;


import com.sun.opengl.util.gl2.*;
import javax.media.opengl.awt.*;

import viewer.*;
import data.*;
import tools.*;

public class Volume{
  public ColorTable ctable;
  VolumeSlicer render2D;
  RenderingWindow rw;
  public Volume(RenderingWindow rw){
    this.rw=rw;
    ctable=new ColorTable(rw);

    render2D=new VolumeSlicer(rw.vconf,ctable);
  }

  public void setVisible2Dplot(){
    render2D.setVisible();
  }

  public void show(){
    rw.gl.glDisable( GL2.GL_LIGHTING );

    rw.gl.glDepthMask(false);
    rw.gl.glCallList(volume_t);
    rw.gl.glDepthMask(true);

    rw.gl.glCallList(contour_t);
    rw.gl.glEnable( GL2.GL_LIGHTING );

    rw.gl.glCallList( isoSurface_t );
  }

  private int volume_t=Const.DISPLAY_LIST_EMPTY;
  private int isoSurface_t=Const.DISPLAY_LIST_EMPTY;
  private int contour_t=Const.DISPLAY_LIST_EMPTY;
  //mesh
  private int nDrawVoxel;
  private float[] drawVoxel;
  private int nAveVoxel;
  private float[] aveVoxel;


  public void make(){
    GL2 gl=rw.gl;
    GLU glu=rw.glu;
    GLUT glut=rw.glut;
    Atoms atoms=rw.atoms;
    ViewConfig vconf=rw.vconf;
    int dataIndex=rw.renderingVolumeDataIndex;


    float[] color={0.0f, 0.0f, 0.0f, 0.0f};
    float dx=1.f/vconf.volDrawMesh[0];
    float dy=1.f/vconf.volDrawMesh[1];
    float dz=1.f/vconf.volDrawMesh[2];
    int ix,iy,iz,ir;
    float[] range={0.f,1.f};


    if(volume_t!=Const.DISPLAY_LIST_EMPTY)gl.glDeleteLists( volume_t, 1);
    if(isoSurface_t!=Const.DISPLAY_LIST_EMPTY)gl.glDeleteLists( isoSurface_t, 1);
    if(contour_t!=Const.DISPLAY_LIST_EMPTY)gl.glDeleteLists( contour_t, 1);

    if(vconf.isVolXY || vconf.isVolXZ || vconf.isVolYZ ||
       vconf.isVolSurface ||vconf.isVolSurface2 || vconf.isVolContour){

      //create voxel data
      if(dataIndex>0){
        //data voxel
        createRawVoxel(dataIndex,atoms,vconf);
        //range[0]=vconf.dataRange[dataIndex-1][0];
        //range[1]=vconf.dataRange[dataIndex-1][1];
        range[0]=vconf.volRange[0];
        range[1]=vconf.volRange[1];
      }else{
        //density voxel
        createDensityVoxelData(atoms,vconf);
        range[0]=0.f;
        range[1]=1.f/nAveVoxel*vconf.volDensityFactor;
      }
    }else{
      return;
    }
    ctable.setRange(range);

    //create rendering voxel
    voxel2DrawVoxel(atoms,vconf);

    //volume
    float drRange=range[1]-range[0];
    float levelMin=vconf.volCutLevel[0]*drRange;
    float levelMax=vconf.volCutLevel[1]*drRange;
    volume_t = gl.glGenLists(1);
    gl.glNewList( volume_t, GL2.GL_COMPILE );
    //xyplane
    for(ix=0;ix<vconf.volDrawMesh[0];ix++){
      for(iy=0;iy<vconf.volDrawMesh[1];iy++){
        for(iz=0;iz<vconf.volDrawMesh[2];iz++){
          ir=iz+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];

          color = ctable.getColor(drawVoxel[ir]);
          //xy plane
          if(vconf.isVolXY){
            if(vconf.isVolCut){
              if(levelMin<levelMax){
                if(levelMin<=drawVoxel[ir] && drawVoxel[ir]<=levelMax)
                  color[3]=1.f/(float)vconf.volDrawMesh[2];
                else
                  color[3]=0.f;
              }else{
                if(drawVoxel[ir]<=levelMin && levelMax<=drawVoxel[ir])
                  color[3]=1.f/(float)vconf.volDrawMesh[2];
                else
                  color[3]=0.f;
              }
            }else{
              color[3]=1.f/(float)vconf.volDrawMesh[2];
            }

            gl.glBegin( GL2.GL_POLYGON );
            gl.glColor4fv(color, 0);
            gl.glMaterialfv( GL2.GL_FRONT_AND_BACK,GL2.GL_AMBIENT_AND_DIFFUSE,color, 0);

            float[] tp=mulH(atoms.hmat,dx*ix,dy*iy,dz*(iz+0.5f));
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*ix,dy*(iy+1),dz*(iz+0.5f));
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*(ix+1), dy*(iy+1), dz*(iz+0.5f));
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*(ix+1), dy*iy,     dz*(iz+0.5f));
            gl.glVertex3fv(tp,0);

            gl.glEnd();
          }
          //xz plane
          if(vconf.isVolXZ){
            if(vconf.isVolCut){
              if(levelMin<levelMax){
                if(levelMin<=drawVoxel[ir] && drawVoxel[ir]<=levelMax)
                  color[3]=1.f/(float)vconf.volDrawMesh[1];
                else
                  color[3]=0.f;
              }else{
                if(drawVoxel[ir]<=levelMin && levelMax<=drawVoxel[ir])
                  color[3]=1.f/(float)vconf.volDrawMesh[1];
                else
                  color[3]=0.f;
              }
            }else{
              color[3]=1.f/(float)vconf.volDrawMesh[1];
            }

            gl.glBegin( GL2.GL_POLYGON );
            gl.glColor4fv(color, 0);
            gl.glMaterialfv( GL2.GL_FRONT_AND_BACK,GL2.GL_AMBIENT_AND_DIFFUSE,color, 0);

            float[] tp=mulH(atoms.hmat,dx*ix,dy*iy, dz*iz);
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*ix,dy*iy, dz*(iz+1));
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*(ix+1), dy*iy, dz*(iz+1));
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*(ix+1), dy*iy, dz*iz);
            gl.glVertex3fv(tp,0);

            gl.glEnd();
          }//xz
          //yx plane
          if(vconf.isVolYZ){
            if(vconf.isVolCut){
              if(levelMin<levelMax){
                if(levelMin<=drawVoxel[ir] && drawVoxel[ir]<=levelMax)
                  color[3]=1.f/(float)vconf.volDrawMesh[0];
                else
                  color[3]=0.f;
              }else{
                if(drawVoxel[ir]<=levelMin && levelMax<=drawVoxel[ir])
                  color[3]=1.f/(float)vconf.volDrawMesh[0];
                else
                  color[3]=0.f;
              }
            }else{
              color[3]=1.f/(float)vconf.volDrawMesh[0];
            }

            gl.glBegin( GL2.GL_POLYGON );
            gl.glColor4fv(color, 0);
            gl.glMaterialfv( GL2.GL_FRONT_AND_BACK,GL2.GL_AMBIENT_AND_DIFFUSE,color, 0);

            float[] tp=mulH(atoms.hmat,dx*ix, dy*iy,dz*iz);
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*ix, dy*iy,dz*(iz+1));
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*ix, dy*(iy+1), dz*(iz+1));
            gl.glVertex3fv(tp,0);
            tp=mulH(atoms.hmat,dx*ix, dy*(iy+1), dz*iz);
            gl.glVertex3fv(tp,0);

            gl.glEnd();
          }

        }//iz
      }//iy
    }//ix
    gl.glEndList();
    //end of volume rendering

    //contour
    contour_t = gl.glGenLists(1);
    gl.glNewList( contour_t, GL2.GL_COMPILE);
    if(vconf.isVolContour) createContour(dataIndex,gl,glu,glut,atoms,vconf);
    gl.glEndList();
    //end of contour

    //isoSurface
    isoSurface_t = gl.glGenLists(1);
    gl.glNewList(isoSurface_t, GL2.GL_COMPILE);
    switch(vconf.volSurfaceRenderType){
    case 0:
      //dot surface
      if(vconf.isVolSurface)createDotSurface(dataIndex,gl,glu,glut,atoms,vconf);
      break;
    case 1:
      //marchingcube method
      if(vconf.isVolSurface){
        float target=vconf.volSurfaceLevel*drRange+range[0];
        make_marching(gl,glu,glut,atoms,vconf,target);
      }
      if(vconf.isVolSurface2){
        float target=vconf.volSurfaceLevel2*drRange+range[0];
        make_marching(gl,glu,glut,atoms,vconf,target);
      }
      break;
    case 2:
      //marchingcube method2
      if(vconf.isVolSurface){
        float target=vconf.volSurfaceLevel*drRange+range[0];
        make_marching2(gl,glu,glut,atoms,vconf,target);
      }
      if(vconf.isVolSurface2){
        float target=vconf.volSurfaceLevel2*drRange+range[0];
        make_marching2(gl,glu,glut,atoms,vconf,target);
      }
      break;
    }
    //end of isoSurface

    gl.glEndList();

  }


  private float[] mulH( float[][] h,float[] in ){
    float[] out = new float[3];
    for(int k=0; k<3; k++)
      out[k] = (h[0][k]*in[0] +h[1][k]*in[1] +h[2][k]*in[2]);

    return out;
  }
  private float[] mulH( float[][] h,float x, float y, float z){
    float[] out = new float[3];
    for(int k=0; k<3; k++)
      out[k] = (h[0][k]*x +h[1][k]*y +h[2][k]*z);

    return out;
  }

  private void createContour(int dataIndex,
                             GL2 gl, GLU glu, GLUT glut,
                             Atoms atoms,ViewConfig vconf){

    int ix,iy,iz;
    float x,y,z;
    float dxMesh=1.f/(float)vconf.volDrawMesh[0];
    float dyMesh=1.f/(float)vconf.volDrawMesh[1];
    float dzMesh=1.f/(float)vconf.volDrawMesh[2];

    float srfLevel;
    if(dataIndex>1){
      srfLevel=vconf.volSurfaceLevel*(vconf.dataRange[dataIndex-1][1]
                                      -vconf.dataRange[dataIndex-1][0]);
    }else{
      srfLevel=vconf.volSurfaceLevel/nAveVoxel*vconf.volDensityFactor;
    }
    gl.glPointSize(vconf.volContourPointSize);
    gl.glBegin(GL2.GL_POINTS);

    //get normal and points from plane panel
    float[] normal=vconf.volContourPlaneNormal;
    //float[] point=mulH(atoms.h,vconf.volContourPlanePoint);
    float[] point=vconf.volContourPlanePoint;

    //primitive vector for 2Dplot
    float[][] pVec=rw.ctrl.getPlanePrimitiveVector(vconf.volContourPlaneNormal,
                                                   vconf.volContourPlanePoint);
    float[] a=pVec[0];
    float[] b=pVec[1];
    float[] c={ a[1]*b[2]-a[2]*b[1],
                a[2]*b[0]-a[0]*b[2],
                a[0]*b[1]-a[1]*b[0]};
    float tmp=(float)Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]);
    c[0]/=tmp;c[1]/=tmp;c[2]/=tmp;
    float[][] basis={{a[0],b[0],c[0]},
                     {a[1],b[1],c[1]},
                     {a[2],b[2],c[2]}};

    float[][] invBasis=new float[3][3];
    Matrix.inv(basis, invBasis);

    point2D.clear();
    range[0]=1.f;//min
    range[1]=-1.f;//max

    //create plane data
    if(normal[2]!=0.f){
      for(int i=0;i<vconf.volDrawMesh[0];i++){
        x=dxMesh*i;
        ix=(int)(x/dxMesh);
        for(int j=0;j<vconf.volDrawMesh[1];j++){
          y=dyMesh*j;
          iy=(int)(y/dyMesh);

          //plane
          z=point[2]-normal[0]*(x-point[0])-normal[1]*(y-point[1]);
          z/=normal[2];
          if(z<0 || 1<z)continue;
          iz=(int)(z/dzMesh);
          int ir=iz+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];

          float[] tp=mulH(atoms.hmat,x,y,z);
          addPoint2D(invBasis,tp,drawVoxel[ir]);
          float[] color=ctable.getColor(drawVoxel[ir]);
          if( Float.isNaN(color[0]) || Float.isNaN(color[1]) ||
              Float.isNaN(color[2]) || Float.isNaN(color[3]) )continue;
          //cut tiny value
          if(vconf.isVolCutTiny && Math.abs(drawVoxel[ir])<1e-3)continue;
          gl.glColor4fv(color, 0);
          gl.glVertex3fv(tp,0);
        }//end of j
      }//end of i
    }else if(normal[1]!=0.f && normal[2]==0.f){
      for(int i=0;i<vconf.volDrawMesh[0];i++){
        x=dxMesh*i;
        ix=(int)(x/dxMesh);
        for(int j=0;j<vconf.volDrawMesh[2];j++){
          z=dzMesh*j;
          iz=(int)(z/dzMesh);

          //plane
          y=point[1]-normal[0]*(x-point[0])-normal[2]*(z-point[2]);
          y/=normal[1];
          if(y<0 || 1<y)continue;
          iy=(int)(y/dyMesh);
          int ir=iz+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];

          float[] tp=mulH(atoms.hmat,x,y,z);
          addPoint2D(invBasis,tp,drawVoxel[ir]);
          float[] color=ctable.getColor(drawVoxel[ir]);
          if( Float.isNaN(color[0]) || Float.isNaN(color[1]) ||
              Float.isNaN(color[2]) || Float.isNaN(color[3]) )continue;
          //cut tiny value
          if(vconf.isVolCutTiny && Math.abs(drawVoxel[ir])<1e-3)continue;

          gl.glColor4fv(color, 0);
          gl.glVertex3fv(tp,0);
        }//end of j
      }//end of i
    }else if(normal[0]!=0.f && normal[1]==0.f && normal[2]==0.f){
      for(int i=0;i<vconf.volDrawMesh[1];i++){
        y=dyMesh*i;
        iy=(int)(y/dyMesh);
        for(int j=0;j<vconf.volDrawMesh[2];j++){
          z=dzMesh*j;
          iz=(int)(z/dzMesh);

          //plane
          x=point[0]-normal[1]*(y-point[1])-normal[2]*(z-point[2]);
          x/=normal[0];
          if(x<0 || 1<x)continue;
          ix=(int)(x/dxMesh);
          int ir=iz+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];

          float[] tp=mulH(atoms.hmat,x,y,z);
          addPoint2D(invBasis,tp,drawVoxel[ir]);
          float[] color=ctable.getColor(drawVoxel[ir]);
          if( Float.isNaN(color[0]) || Float.isNaN(color[1]) ||
              Float.isNaN(color[2]) || Float.isNaN(color[3]) )continue;
          //cut tiny value
          if(vconf.isVolCutTiny && Math.abs(drawVoxel[ir])<1e-3)continue;
          gl.glColor4fv(color, 0);
          gl.glVertex3fv(tp,0);
        }//end of j
      }//end of i
    }else{
      System.out.println("error in Miller plane");
      return;
    }

    gl.glEnd();
    render2D.setData(pVec,point2D,range);
  }

  private float[] range=new float[2];
  private ArrayList<Float> point2D= new ArrayList<Float>();
  // add point to point2D
  private void addPoint2D(float[][] ei,float[] tp,float val){
    point2D.add(tp[0]*ei[0][0]+tp[1]*ei[0][1]+tp[2]*ei[0][2]);
    point2D.add(tp[0]*ei[1][0]+tp[1]*ei[1][1]+tp[2]*ei[1][2]);
    point2D.add(val);
    if(range[0]>val)range[0]=val;
    if(range[1]<val)range[1]=val;
  }

  private void createDensityVoxelData(Atoms atoms,ViewConfig vconf){
    nAveVoxel=vconf.volDataMesh[0]*vconf.volDataMesh[1]*vconf.volDataMesh[2];
    aveVoxel=null;
    aveVoxel=new float[nAveVoxel];
    for(int i=0;i<nAveVoxel;i++)aveVoxel[i]=0.f;

    int ix,iy,iz,ir;
    float dxData=atoms.hmat[0][0]/(float)vconf.volDataMesh[0];
    float dyData=atoms.hmat[1][1]/(float)vconf.volDataMesh[1];
    float dzData=atoms.hmat[2][2]/(float)vconf.volDataMesh[2];
    int natm= atoms.getNumAtoms();
    for(int i=0;i<natm;i++){
      Atom ai= atoms.getAtom(i);
      ix=(int)(ai.pos[0]/dxData);
      iy=(int)(ai.pos[1]/dyData);
      iz=(int)(ai.pos[2]/dzData);
      if(ix>=vconf.volDataMesh[0])ix=vconf.volDataMesh[0]-1;
      if(iy>=vconf.volDataMesh[1])iy=vconf.volDataMesh[1]-1;
      if(iz>=vconf.volDataMesh[2])iz=vconf.volDataMesh[2]-1;
      ir=iz+iy*vconf.volDataMesh[2]+ix*vconf.volDataMesh[2]*vconf.volDataMesh[1];
      aveVoxel[ir]+=1.f/natm;
    }
  }

  void createRawVoxel(int dataIndex,Atoms atoms,ViewConfig vconf){

    nAveVoxel=(vconf.volDataMesh[0]+1)*(vconf.volDataMesh[1]+1)*(vconf.volDataMesh[2]+1);
    aveVoxel=null;
    aveVoxel=new float[nAveVoxel];
    int natm= atoms.getNumAtoms();
    //make fundamental aveVoxel data
    int nlist= natm+nAveVoxel; // [0:nAtoms-1]=body, [nAtoms:]=header.
    int[] llist= new int[nlist];
    PairList.makeLinkedList(vconf.volDataMesh[0],vconf.volDataMesh[1],vconf.volDataMesh[2],
                            nlist,llist,atoms,false);


    // region-ir
    for(int ir=0; ir<nAveVoxel; ir++){
      // atom-i in region-ir
      int i= llist[natm +ir];//header
      int n=0;
      Atom ai= atoms.getAtom(i);

      switch(rw.renderingVolumeType){
      case 0://only volume
        while(i>=0){
          if( ai.tag==Const.VOLUME_DATA_TAG){
            aveVoxel[ir]+= ai.auxData[dataIndex-1];
            n++;
          }
          i= llist[i];// next-i in region-ir
        }
        break;
      case 1:// both atom, volume
        while(i>=0){
          aveVoxel[ir]+= ai.auxData[dataIndex-1];
          n++;
          i= llist[i];// next-i in region-ir
        }
        break;
      }
      aveVoxel[ir]/=(float)n;
    }
    llist=null;
  }

  private void voxel2DrawVoxel( Atoms atoms,ViewConfig vconf ){
    //create draw voxel
    nDrawVoxel=vconf.volDrawMesh[0]*vconf.volDrawMesh[1]*vconf.volDrawMesh[2];
    drawVoxel=null;
    drawVoxel=new float[nDrawVoxel];
    //interpolation
    float x,y,z;
    float dxMesh=1.f/(float)vconf.volDrawMesh[0];
    float dyMesh=1.f/(float)vconf.volDrawMesh[1];
    float dzMesh=1.f/(float)vconf.volDrawMesh[2];
    float dxData=1.f/(float)vconf.volDataMesh[0];
    float dyData=1.f/(float)vconf.volDataMesh[1];
    float dzData=1.f/(float)vconf.volDataMesh[2];

    int i,j,k,ix,iy,iz,im;
    int id,idx,idy,idz,idxy,idxz,idyz,idxyz;
    for(i=0;i<vconf.volDrawMesh[0];i++){
      x=dxMesh*(i+0.f);
      ix=(int)(x/dxData);
      for(j=0;j<vconf.volDrawMesh[1];j++){
        y=dyMesh*(j+0.f);
        iy=(int)(y/dyData);
        for(k=0;k<vconf.volDrawMesh[2];k++){
          z=dzMesh*(k+0.f);
          iz=(int)(z/dzData);

          //data scalar index
          id=iz+iy*vconf.volDataMesh[2]+ix*vconf.volDataMesh[2]*vconf.volDataMesh[1];

          int ixp=ix+1;
          int iyp=iy+1;
          int izp=iz+1;

          if(ixp>=vconf.volDataMesh[0])ixp=0;
          if(iyp>=vconf.volDataMesh[1])iyp=0;
          if(izp>=vconf.volDataMesh[2])izp=0;

          idx=iz+iy*vconf.volDataMesh[2]+ixp*vconf.volDataMesh[2]*vconf.volDataMesh[1];
          idy=iz+iyp*vconf.volDataMesh[2]+ix*vconf.volDataMesh[2]*vconf.volDataMesh[1];
          idz=izp+iy*vconf.volDataMesh[2]+ix*vconf.volDataMesh[2]*vconf.volDataMesh[1];
          idxy=iz+iyp*vconf.volDataMesh[2]+ixp*vconf.volDataMesh[2]*vconf.volDataMesh[1];
          idxz=izp+iy*vconf.volDataMesh[2]+ixp*vconf.volDataMesh[2]*vconf.volDataMesh[1];
          idyz=izp+iyp*vconf.volDataMesh[2]+ix*vconf.volDataMesh[2]*vconf.volDataMesh[1];
          idxyz=izp+iyp*vconf.volDataMesh[2]+ixp*vconf.volDataMesh[2]*vconf.volDataMesh[1];

          //mesh scalar index
          im=k+j*vconf.volDrawMesh[2]+i*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];

          float alpha_p= (x/dxData-ix);
          float alpha_m= 1.f-alpha_p;
          float beta_p = y/dyData-iy;
          float beta_m = 1.f-beta_p;
          float gamma_p= z/dzData-iz;
          float gamma_m= 1.f-gamma_p;

          drawVoxel[im]=
            aveVoxel[id]*alpha_m*beta_m*gamma_m
            +aveVoxel[idx]*alpha_p*beta_m*gamma_m
            +aveVoxel[idy]*alpha_m*beta_p*gamma_m
            +aveVoxel[idz]*alpha_m*beta_m*gamma_p
            +aveVoxel[idxy]*alpha_p*beta_p*gamma_m
            +aveVoxel[idxz]*alpha_p*beta_m*gamma_p
            +aveVoxel[idyz]*alpha_m*beta_p*gamma_p
            +aveVoxel[idxyz]*alpha_p*beta_p*gamma_p;
        }//end of k
      }//end of j
    }//end of i
  }


  void createDotSurface(int dataIndex,
                        GL2 gl, GLU glu, GLUT glut,
                        Atoms atoms,ViewConfig vconf){
    int ix,iy,iz,jx,jy,jz,ic,jc;
    float x,y,z;
    float dx=1.f/vconf.volDrawMesh[0];
    float dy=1.f/vconf.volDrawMesh[1];
    float dz=1.f/vconf.volDrawMesh[2];
    float[][] h= atoms.hmat;

    float srfLevel=vconf.volSurfaceLevel*(vconf.dataRange[dataIndex-1][1]
                                          -vconf.dataRange[dataIndex-1][0]);


    gl.glPointSize(2);
    gl.glBegin(GL2.GL_POINTS);
    gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE,
                    ctable.getColor(srfLevel), 0 );

    for(ix=0; ix<vconf.volDrawMesh[0]; ix++){
      for(iy=0; iy<vconf.volDrawMesh[1]; iy++){
        for(iz=0; iz<vconf.volDrawMesh[2]; iz++){
          ic=iz+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
          //search vconf.volSurfaceNeighbors
          for(jx=ix-vconf.volSurfaceNeighbors;jx<=ix+vconf.volSurfaceNeighbors;jx++){
            if(jx<0||jx>=vconf.volDrawMesh[0])continue;
            for(jy=iy-vconf.volSurfaceNeighbors;jy<=iy+vconf.volSurfaceNeighbors;jy++){
              if(jy<0||jy>=vconf.volDrawMesh[1])continue;
              for(jz=iz-vconf.volSurfaceNeighbors;jz<=iz+vconf.volSurfaceNeighbors;jz++){
                if(jz<0||jz>=vconf.volDrawMesh[2])continue;
                //neighbor cell
                jc=jz+jy*vconf.volDrawMesh[2]+jx*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
                if(ic==jc || jc>vconf.volDrawMesh[0]*vconf.volDrawMesh[1]*vconf.volDrawMesh[2] || jc<0)continue;
                //interpolation
                if(drawVoxel[ic]<=srfLevel && srfLevel<=drawVoxel[jc]){
                  float[] tpi=mulH(h,dx*ix,dy*iy,dz*iz);
                  float[] tpj=mulH(h,dx*jx,dy*jy,dz*jz);

                  x=(tpi[0]*(drawVoxel[jc]-srfLevel)+tpj[0]*(srfLevel-drawVoxel[ic]))
                    /(drawVoxel[jc]-drawVoxel[ic]);
                  y=(tpi[1]*(drawVoxel[jc]-srfLevel)+tpj[1]*(srfLevel-drawVoxel[ic]))
                    /(drawVoxel[jc]-drawVoxel[ic]);
                  z=(tpi[2]*(drawVoxel[jc]-srfLevel)+tpj[2]*(srfLevel-drawVoxel[ic]))
                    /(drawVoxel[jc]-drawVoxel[ic]);

                  gl.glVertex3f(x,y,z);
                }
              }//jz
            }//jy
          }//jx

        }//end of iz
      }//end of iy
    }//end of ix
    gl.glEnd();

  }



  //////////////////////////////////////
  // MARCHING CUBE
  //////////////////////////////////////
  // order of 8 verterx is below
  // (0,0,0),(1,0,0),(1,1,0),(0,1,0),(0,0,1),(1,0,1),(1,1,1),(0,1,1)

  //fGetOffset finds the approximate point of intersection of the surface
  // between two points with the values fValue1 and fValue2
  float fGetOffset(float fValue1, float fValue2, float fValueDesired){
    double fDelta = fValue2 - fValue1;
    if(fDelta == 0.0)return 0.5f;
    return (float)((fValueDesired - fValue1)/fDelta);
  }

  float[] calNormal(Vector v1,Vector v2,Vector v3){
    float x1=v2.fX-v1.fX;
    float y1=v2.fY-v1.fY;
    float z1=v2.fZ-v1.fZ;
    float x2=v3.fX-v1.fX;
    float y2=v3.fY-v1.fY;
    float z2=v3.fZ-v1.fZ;

    float[] nr={y1*z2-z1*y2,
                z1*x2-x1*z2,
                x1*y2-y1*x2};
    float r=(float)Math.sqrt(nr[0]*nr[0]+nr[1]*nr[1]+nr[2]*nr[2]);
    if(r>0.000001f){
      nr[0]/=r;
      nr[1]/=r;
      nr[2]/=r;
    }
    return nr;
  }

  public void make_marching(GL2 gl, GLU glu, GLUT glut,
                            Atoms atoms,ViewConfig vconf,
                            float range){

    //gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
    gl.glBegin(GL.GL_TRIANGLES);
    for(int iX = 0; iX < vconf.volDrawMesh[0]-1; iX++)
      for(int iY = 0; iY < vconf.volDrawMesh[1]-1; iY++)
        for(int iZ = 0; iZ < vconf.volDrawMesh[2]-1; iZ++)
          vMarchCube(gl, glu, glut, atoms,vconf,iX, iY, iZ,range);
    gl.glEnd();
  }
  public void make_marching2(GL2 gl, GLU glu, GLUT glut,
                             Atoms atoms, ViewConfig vconf,
                             float range){

    gl.glBegin(GL.GL_TRIANGLES);
    for(int iX = 0; iX < vconf.volDrawMesh[0]-1; iX++)
      for(int iY = 0; iY < vconf.volDrawMesh[1]-1; iY++)
        for(int iZ = 0; iZ < vconf.volDrawMesh[2]-1; iZ++)
          vMarchCube2(gl, glu, glut, atoms,vconf,iX, iY, iZ,range);
    gl.glEnd();
  }

  //vMarchCube performs the Marching Cubes algorithm on a single cube
  void vMarchCube(GL2 gl, GLU glu, GLUT glut,
                  Atoms atoms,ViewConfig vconf,
                  int ix,int iy,int iz,float range){

    float fTargetValue=range;

    int iCorner, iVertex, iVertexTest, iEdge, iTriangle, iFlagIndex, iEdgeFlags;
    float fOffset;
    Vector sColor;
    float[] afCubeValue=new float[8];

    Vector[] asEdgeVertex=new Vector[12];
    Vector[] asEdgeNorm=new Vector[12];
    for(int i=0;i<12;i++)asEdgeVertex[i]=new Vector(0.f,0.f,0.f);
    for(int i=0;i<12;i++)asEdgeNorm[i]=new Vector(0.f,0.f,0.f);

    //Make a local copy of the values at the cube's corners
    int ir;
    ir=iz+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[0] =drawVoxel[ir];
    ir=iz+iy*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[1] =drawVoxel[ir];
    ir=iz+(iy+1)*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[2] =drawVoxel[ir];
    ir=iz+(iy+1)*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[3] =drawVoxel[ir];

    ir=iz+1+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[4] =drawVoxel[ir];
    ir=iz+1+iy*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[5] =drawVoxel[ir];
    ir=iz+1+(iy+1)*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[6] =drawVoxel[ir];
    ir=iz+1+(iy+1)*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[7] =drawVoxel[ir];



    //Find which vertices are inside of the surface and which are outside
    iFlagIndex = 0;
    for(iVertexTest = 0; iVertexTest < 8; iVertexTest++){
      if(afCubeValue[iVertexTest] <= fTargetValue)iFlagIndex |= 1<<iVertexTest;
    }

    //Find which edges are intersected by the surface
    iEdgeFlags = aiCubeEdgeFlags[iFlagIndex];

    //If the cube is entirely inside or outside of the surface, then there will be no intersections
    if(iEdgeFlags == 0)return;

    //Find the point of intersection of the surface with each edge
    //Then find the normal to the surface at those points
    for(iEdge = 0; iEdge < 12; iEdge++){
      //if there is an intersection on this edge

      if((iEdgeFlags & (1<<iEdge))>0){
        fOffset = fGetOffset(afCubeValue[ a2iEdgeConnection[iEdge][0] ],
                             afCubeValue[ a2iEdgeConnection[iEdge][1] ],
                             fTargetValue);

        float dx=1.f/vconf.volDrawMesh[0];
        float dy=1.f/vconf.volDrawMesh[1];
        float dz=1.f/vconf.volDrawMesh[2];
        float fX=dx*ix;
        float fY=dy*iy;
        float fZ=dz*iz;
        float[] tp=mulH(atoms.hmat,
                        fX + dx*(a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][0]  +  fOffset * a2fEdgeDirection[iEdge][0])  ,
                        fY + dy*(a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][1]  +  fOffset * a2fEdgeDirection[iEdge][1])  ,
                        fZ + dz*(a2fVertexOffset[ a2iEdgeConnection[iEdge][0] ][2]  +  fOffset * a2fEdgeDirection[iEdge][2]) );
        asEdgeVertex[iEdge].set(tp[0],tp[1],tp[2]);

      }
    }


    //Draw the triangles that were found.  There can be up to five per cube
    for(iTriangle = 0; iTriangle < 5; iTriangle++){
      if(a2iTriangleConnectionTable[iFlagIndex][3*iTriangle] < 0)break;

      int i1= a2iTriangleConnectionTable[iFlagIndex][3*iTriangle];
      int i2= a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+1];
      int i3= a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+2];
      float[] normal=calNormal(asEdgeVertex[i1],
                               asEdgeVertex[i2],
                               asEdgeVertex[i3]);
      for(iCorner = 0; iCorner < 3; iCorner++){
        iVertex = a2iTriangleConnectionTable[iFlagIndex][3*iTriangle+iCorner];

        //gl.glColor4fv(ctable.getColor(fTargetValue), 0 );
        gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE,ctable.getColor(fTargetValue), 0 );

        if(fTargetValue<0)
          gl.glNormal3f(-normal[0],-normal[1],-normal[2]);
        else
          gl.glNormal3f(normal[0],normal[1],normal[2]);
        gl.glVertex3f(asEdgeVertex[iVertex].fX,
                      asEdgeVertex[iVertex].fY,
                      asEdgeVertex[iVertex].fZ);
      }
    }
  }

  //vMarchCube2 performs the Marching Tetrahedrons algorithm on a single cube by making six calls to vMarchTetrahedron
  void vMarchCube2(GL2 gl, GLU glu, GLUT glut,
                   Atoms atoms,ViewConfig vconf,
                   int ix,int iy,int iz,float range){
    float dx=1.f/vconf.volDrawMesh[0];
    float dy=1.f/vconf.volDrawMesh[1];
    float dz=1.f/vconf.volDrawMesh[2];
    float tp[]=new float[3];
    int iVertex, iTetrahedron, iVertexInACube;
    float[] afCubeValue=new float[8];
    float[] afTetrahedronValue=new float[4];

    Vector[] asCubePosition=new Vector[8];
    Vector[] asTetrahedronPosition=new Vector[4];
    for(int i=0;i<8;i++)asCubePosition[i]=new Vector(0.f,0.f,0.f);
    for(int i=0;i<4;i++)asTetrahedronPosition[i]=new Vector(0.f,0.f,0.f);

    //Make a local copy of the cube's corner positions
    //Make a local copy of the cube's corner values
    int ir;
    ir=iz+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[0] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*ix,dy*iy,dz*iz);
    asCubePosition[0].set(tp[0],tp[1],tp[2]);

    ir=iz+iy*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[1] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*(ix+1),dy*iy,dz*iz);
    asCubePosition[1].set(tp[0],tp[1],tp[2]);

    ir=iz+(iy+1)*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[2] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*(ix+1),dy*(iy+1),dz*iz);
    asCubePosition[2].set(tp[0],tp[1],tp[2]);

    ir=iz+(iy+1)*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[3] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*ix,dy*(iy+1),dz*iz);
    asCubePosition[3].set(tp[0],tp[1],tp[2]);

    ir=iz+1+iy*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[4] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*ix,dy*iy,dz*(iz+1));
    asCubePosition[4].set(tp[0],tp[1],tp[2]);

    ir=iz+1+iy*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[5] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*(ix+1),dy*iy,dz*(iz+1));
    asCubePosition[5].set(tp[0],tp[1],tp[2]);

    ir=iz+1+(iy+1)*vconf.volDrawMesh[2]+(ix+1)*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[6] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*(ix+1),dy*(iy+1),dz*(iz+1));
    asCubePosition[6].set(tp[0],tp[1],tp[2]);

    ir=iz+1+(iy+1)*vconf.volDrawMesh[2]+ix*vconf.volDrawMesh[2]*vconf.volDrawMesh[1];
    afCubeValue[7] =drawVoxel[ir];
    tp=mulH(atoms.hmat,dx*ix,dy*(iy+1),dz*(iz+1));
    asCubePosition[7].set(tp[0],tp[1],tp[2]);

    for(iTetrahedron = 0; iTetrahedron < 6; iTetrahedron++){
      for(iVertex = 0; iVertex < 4; iVertex++){
        iVertexInACube = a2iTetrahedronsInACube[iTetrahedron][iVertex];
        asTetrahedronPosition[iVertex].fX = asCubePosition[iVertexInACube].fX;
        asTetrahedronPosition[iVertex].fY = asCubePosition[iVertexInACube].fY;
        asTetrahedronPosition[iVertex].fZ = asCubePosition[iVertexInACube].fZ;
        afTetrahedronValue[iVertex] = afCubeValue[iVertexInACube];
      }
      vMarchTetrahedron(gl,glu,glut,vconf,asTetrahedronPosition, afTetrahedronValue,range);
    }
  }

  //vMarchTetrahedron performs the Marching Tetrahedrons algorithm on a single tetrahedron
  void vMarchTetrahedron(GL2 gl, GLU glu, GLUT glut,ViewConfig vconf,
                         Vector[] pasTetrahedronPosition, float[] pafTetrahedronValue,float range){
    float fTargetValue=range;

    int iEdge, iVert0, iVert1, iEdgeFlags, iTriangle, iCorner, iVertex, iFlagIndex = 0;
    float fOffset, fInvOffset, fValue = 0.0f;

    Vector sColor=new Vector(0.f,0.f,0.f);

    Vector[] asEdgeVertex=new Vector[12];
    Vector[] asEdgeNorm=new Vector[12];
    for(int i=0;i<12;i++)asEdgeVertex[i]=new Vector(0.f,0.f,0.f);
    for(int i=0;i<12;i++)asEdgeNorm[i]=new Vector(0.f,0.f,0.f);

    //Find which vertices are inside of the surface and which are outside
    for(iVertex = 0; iVertex < 4; iVertex++){
      if(pafTetrahedronValue[iVertex] <= fTargetValue)iFlagIndex |= 1<<iVertex;
    }

    //Find which edges are intersected by the surface
    iEdgeFlags = aiTetrahedronEdgeFlags[iFlagIndex];

    //If the tetrahedron is entirely inside or outside of the surface, then there will be no intersections
    if(iEdgeFlags == 0)return;

    //Find the point of intersection of the surface with each edge
    // Then find the normal to the surface at those points
    for(iEdge = 0; iEdge < 6; iEdge++){
      //if there is an intersection on this edge
      if((iEdgeFlags & (1<<iEdge))>0){
        iVert0 = a2iTetrahedronEdgeConnection[iEdge][0];
        iVert1 = a2iTetrahedronEdgeConnection[iEdge][1];
        fOffset = fGetOffset(pafTetrahedronValue[iVert0], pafTetrahedronValue[iVert1], fTargetValue);
        fInvOffset = 1.0f - fOffset;

        asEdgeVertex[iEdge].fX = fInvOffset*pasTetrahedronPosition[iVert0].fX  +  fOffset*pasTetrahedronPosition[iVert1].fX;
        asEdgeVertex[iEdge].fY = fInvOffset*pasTetrahedronPosition[iVert0].fY  +  fOffset*pasTetrahedronPosition[iVert1].fY;
        asEdgeVertex[iEdge].fZ = fInvOffset*pasTetrahedronPosition[iVert0].fZ  +  fOffset*pasTetrahedronPosition[iVert1].fZ;
      }
    }

    //Draw the triangles that were found.  There can be up to 2 per tetrahedron
    for(iTriangle = 0; iTriangle < 2; iTriangle++){
      if(a2iTetrahedronTriangles[iFlagIndex][3*iTriangle] < 0)break;

      int i1= a2iTetrahedronTriangles[iFlagIndex][3*iTriangle];
      int i2= a2iTetrahedronTriangles[iFlagIndex][3*iTriangle+1];
      int i3= a2iTetrahedronTriangles[iFlagIndex][3*iTriangle+2];
      float[] normal=calNormal(asEdgeVertex[i1],
                               asEdgeVertex[i2],
                               asEdgeVertex[i3]);

      for(iCorner = 0; iCorner < 3; iCorner++){
        iVertex = a2iTetrahedronTriangles[iFlagIndex][3*iTriangle+iCorner];

        //gl.glColor4fv(ctable.getColor(fTargetValue), 0 );
        gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE,ctable.getColor(fTargetValue), 0 );
        if(fTargetValue<0)
          gl.glNormal3f(-normal[0],-normal[1],-normal[2]);
        else
          gl.glNormal3f(normal[0],normal[1],normal[2]);
        gl.glVertex3f(asEdgeVertex[iVertex].fX, asEdgeVertex[iVertex].fY, asEdgeVertex[iVertex].fZ);
      }
    }
  }



  //These tables are used so that everything can be done in little loops that you can
  // look at all at once rather than in pages and pages of unrolled code.

  //a2fVertexOffset lists the positions, relative to vertex0,
  // of each of the 8 vertices of a cube
  float[][] a2fVertexOffset ={//[8][3]
    {0.0f, 0.0f, 0.0f},{1.0f, 0.0f, 0.0f},{1.0f, 1.0f, 0.0f},{0.0f, 1.0f, 0.0f},
    {0.0f, 0.0f, 1.0f},{1.0f, 0.0f, 1.0f},{1.0f, 1.0f, 1.0f},{0.0f, 1.0f, 1.0f}
  };

  //a2iEdgeConnection lists the index of the endpoint vertices
  // for each of the 12 edges of the cube
  int[][] a2iEdgeConnection ={//[12][2]
    {0,1}, {1,2}, {2,3}, {3,0},
    {4,5}, {5,6}, {6,7}, {7,4},
    {0,4}, {1,5}, {2,6}, {3,7}
  };

  //a2fEdgeDirection lists the direction vector (vertex1-vertex0)
  // for each edge in the cube
  float[][] a2fEdgeDirection ={//[12][3]
    {1.0f, 0.0f, 0.0f},{0.0f, 1.0f, 0.0f},{-1.0f, 0.0f, 0.0f},{0.0f, -1.0f, 0.0f},
    {1.0f, 0.0f, 0.0f},{0.0f, 1.0f, 0.0f},{-1.0f, 0.0f, 0.0f},{0.0f, -1.0f, 0.0f},
    {0.0f, 0.0f, 1.0f},{0.0f, 0.0f, 1.0f},{ 0.0f, 0.0f, 1.0f},{0.0f,  0.0f, 1.0f}
  };

  //a2iTetrahedronEdgeConnection lists the index of the endpoint
  // vertices for each of the 6 edges of the tetrahedron
  int[][] a2iTetrahedronEdgeConnection ={//[6][2]
    {0,1},  {1,2},  {2,0},  {0,3},  {1,3},  {2,3}
  };

  //a2iTetrahedronEdgeConnection lists the index of verticies from a cube
  // that made up each of the six tetrahedrons within the cube
  int[][] a2iTetrahedronsInACube ={//[6][4]
    {0,5,1,6},
    {0,1,2,6},
    {0,2,3,6},
    {0,3,7,6},
    {0,7,4,6},
    {0,4,5,6},
  };


  // For any edge,
  //if one vertex is inside of the surface and the other is outside of the surface
  //  then the edge intersects the surface
  // For each of the 4 vertices of the tetrahedron can be two possible states :
  // either inside or outside of the surface
  // For any tetrahedron the are 2^4=16 possible sets of vertex states
  // This table lists the edges intersected by the surface
  //for all 16 possible vertex states
  // There are 6 edges.  For each entry in the table,
  //if edge #n is intersected, then bit #n is set to 1

  int[] aiTetrahedronEdgeFlags={//[16]
    0x00, 0x0d, 0x13, 0x1e,
    0x26, 0x2b, 0x35, 0x38,
    0x38, 0x35, 0x2b, 0x26,
    0x1e, 0x13, 0x0d, 0x00,
  };


  // For each of the possible vertex states listed
  //in aiTetrahedronEdgeFlags there is a specific triangulation
  // of the edge intersection points.
  //a2iTetrahedronTriangles lists all of them in the form of
  // 0-2 edge triples with the list terminated by the invalid value -1.
  //
  // I generated this table by hand

  int[][] a2iTetrahedronTriangles ={//[16][7]
    {-1, -1, -1, -1, -1, -1, -1},
    { 0,  3,  2, -1, -1, -1, -1},
    { 0,  1,  4, -1, -1, -1, -1},
    { 1,  4,  2,  2,  4,  3, -1},

    { 1,  2,  5, -1, -1, -1, -1},
    { 0,  3,  5,  0,  5,  1, -1},
    { 0,  2,  5,  0,  5,  4, -1},
    { 5,  4,  3, -1, -1, -1, -1},

    { 3,  4,  5, -1, -1, -1, -1},
    { 4,  5,  0,  5,  2,  0, -1},
    { 1,  5,  0,  5,  3,  0, -1},
    { 5,  2,  1, -1, -1, -1, -1},

    { 3,  4,  2,  2,  4,  1, -1},
    { 4,  1,  0, -1, -1, -1, -1},
    { 2,  3,  0, -1, -1, -1, -1},
    {-1, -1, -1, -1, -1, -1, -1},
  };

  // For any edge,
  //if one vertex is inside of the surface and the other is outside of the surface
  //  then the edge intersects the surface
  // For each of the 8 vertices of the cube can be two possible states :
  //either inside or outside of the surface
  // For any cube the are 2^8=256 possible sets of vertex states
  // This table lists the edges intersected by the surface
  //for all 256 possible vertex states
  // There are 12 edges.  For each entry in the table,
  //if edge #n is intersected, then bit #n is set to 1

  int[] aiCubeEdgeFlags={//[256]
    0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905,
    0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x099, 0x393, 0x29a,
    0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93,
    0xf99, 0xe90, 0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
    0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9,
    0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6,
    0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f,
    0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
    0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc, 0xdfc, 0xcf5,
    0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a,
    0x256, 0x35f, 0x055, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53,
    0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc,
    0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9,
    0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0x0cc, 0x1c5, 0x2cf, 0x3c6,
    0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f,
    0xf55, 0xe5c, 0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
    0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5,
    0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a,
    0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663,
    0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
    0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39,
    0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636,
    0x13a, 0x033, 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f,
    0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190,
    0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605,
    0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
  };

  //  For each of the possible vertex states listed
  //in aiCubeEdgeFlags there is a specific triangulation
  //  of the edge intersection points.
  //a2iTriangleConnectionTable lists all of them in the form of
  //  0-5 edge triples with the list terminated by the invalid value -1.
  //  For example: a2iTriangleConnectionTable[3] list the 2 triangles
  //formed when corner[0]
  //  and corner[1] are inside of the surface, but the rest of the cube is not.
  //
  //  I found this table in an example program someone wrote long ago.
  //It was probably generated by hand

  int[][] a2iTriangleConnectionTable ={//[256][16]
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
    {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
    {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
    {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
    {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
    {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
    {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
    {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
    {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
    {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
    {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
    {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
    {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
    {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
    {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
    {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
    {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
    {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
    {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
    {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
    {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
    {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
    {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
    {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
    {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
    {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
    {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
    {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
    {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
    {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
    {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
    {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
    {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
    {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
    {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
    {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
    {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
    {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
    {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
    {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
    {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
    {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
    {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
    {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
    {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
    {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
    {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
    {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
    {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
    {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
    {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
    {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
    {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
    {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
    {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
    {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
    {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
    {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
    {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
    {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
    {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
    {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
    {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
    {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
    {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
    {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
    {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
    {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
    {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
    {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
    {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
    {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
    {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
    {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
    {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
    {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
    {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
    {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
    {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
    {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
    {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
    {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
    {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
    {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
    {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
    {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
    {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
    {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
    {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
    {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
    {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
    {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
    {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
    {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
    {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
    {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
    {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
    {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
    {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
    {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
    {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
    {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
    {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
    {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
    {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
    {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
    {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
    {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
    {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
    {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
    {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
    {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
    {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
    {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
    {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
    {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
    {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
    {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
    {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
    {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
    {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
    {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
    {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
    {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
    {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
    {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
    {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
    {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
    {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
    {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
    {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
    {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
    {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
    {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
    {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
    {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
    {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
    {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
    {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
    {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
    {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
    {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
    {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
    {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
    {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
    {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
    {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
    {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
    {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
    {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
    {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
    {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
    {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
    {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
    {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
    {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
    {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
    {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
    {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
    {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
    {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
    {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
    {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
    {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
    {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
    {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
    {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
    {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
    {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
  };


  class Vector{
    public float fX,fY,fZ;
    public Vector(float x,float y,float z){
      this.fX=x;
      this.fY=y;
      this.fZ=z;
    }
    public void set(float x,float y,float z){
      this.fX=x;
      this.fY=y;
      this.fZ=z;
    }
  }
}
